package com.demo.mvcframework.v1.servlet;

import com.demo.mvcframework.annotation.LZBAutowired;
import com.demo.mvcframework.annotation.LZBController;
import com.demo.mvcframework.annotation.LZBRequestMapping;
import com.demo.mvcframework.annotation.LZBService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * @auther LiZiBa
 * @date 2020/8/11 23:15
 * @description: 模仿手写spring
 **/
public class XLDispatchServlet extends HttpServlet {

    private Properties contextConfig = new Properties();
    // IoC容器,key默认是类名首字母小写，value就是对应的实例对象
    private Map<String, Object> ioc= new HashMap<String, Object>();
    // 享元模式，缓存
    private List<String> classNames = new ArrayList<String>();
    // url请求与方法映射
    private Map<String, Method> handleMapping = new HashMap<String, Method>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 在web.xml中定义contextConfigLocation参数，Spring会使用这个参数去加载所有逗号分隔的xml文件，如果没有这个参数，Spring默认加载web-inf/applicationContext.xml文件。
        // 1.加载配置文件
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        // 2.扫描相关类
        doSacnner(contextConfig.getProperty("scanPackage"));
        // 3.初始化IoC容器
        doInstance();
        // AOP 动态代理

        // DI
        doAutowried();
        // 初始化HandleMapping
        doInitHandleMapping();
    }

    /**
     *初始化HandleMapping
     */
    private void doInitHandleMapping() {

        if(ioc.isEmpty()){
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            // 判断是否添加LZBController注解
            if(!clazz.isAnnotationPresent(LZBController.class)){
                continue;
            }

            // 提取注解上的url
            String baseUrl = "";
            if (clazz.isAnnotationPresent(LZBController.class)) {
                baseUrl = clazz.getAnnotation(LZBController.class).value();
            }

            // 获取public方法
            for(Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(LZBRequestMapping.class)){
                    continue;
                }

                String url = ("/" + baseUrl + "/" + method.getAnnotation(LZBRequestMapping.class).value()).replace("/+", "/") ;
                handleMapping.put(url, method);
            }
        }
    }

    /**
     * 依赖注入
     */
    private void doAutowried() {

        if(ioc.isEmpty()){
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()){
            // 包所以字段取出
            for(Field field : entry.getValue().getClass().getDeclaredFields()){
                // 判断是否需要自动注入
                if(!field.isAnnotationPresent(LZBAutowired.class)){
                    continue;
                }
                String beanName = field.getAnnotation(LZBAutowired.class).value().trim();
                if("".equals(beanName)){
                    beanName = field.getType().getName();
                }

                // 跳过语法校验
                field.setAccessible(true);

                try {
                    // ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                    // Java Field.set()向对象的这个Field属性设置新值value
                    field.set(entry.getValue(), ioc.get(beanName));
                }catch (Exception e){
                    e.printStackTrace();
                }
            }

        }

    }


    /**
     * 初始化容器
     */
    private void doInstance() {

        if(classNames.isEmpty()){
            return;
        }


        try {
            for(String className : classNames){
                Class<?> clazz = Class.forName(className);

                if(clazz.isAnnotationPresent(LZBController.class)){

                    // 提取key,获取类名并且将其首字母小写 value为当前类的实例对象
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);
                }else if(clazz.isAnnotationPresent(LZBService.class)){

                    // 多个包下出现相同的类名，只能自己定义一个全局唯一的名称
                    String beanName = clazz.getAnnotation(LZBService.class).value();
                    if("".equals(beanName.trim())){
                        beanName = toLowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);

                    // 如果是接口
                    // 接口实现类有多个抛出异常，无法判断指向哪个实现类
                    // getInterfaces()方法和Java的反射机制有关它能够获得这个对象所实现的接口。
                    for (Class<?> i : clazz.getInterfaces()) {
                        if(ioc.containsKey(i.getName())){
                            throw new Exception("The" + i.getName() + "is exists!");
                        }
                        ioc.put(i.getName(), instance);
                    }
                }else {
                    continue;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 类名转换为首字母小写
     * @param simpleName
     */
    private String toLowerFirstCase(String simpleName) {

        char[] chars = simpleName.toCharArray();
        // 转换为小写
        chars [0] += 32;
        return String.valueOf(chars);
    }


    /**
     * 扫描指定路径下的相关类
     * @param scanPackage
     */
    private void doSacnner(String scanPackage) {

        // 获取配置的扫描路径的URL
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());

        for (File file : classPath.listFiles()) {

            if(file.isDirectory()){
                // 获取的文件夹继续遍历
                doSacnner(scanPackage + "." + file.getName());
            }else {
                // 只处理java文件
                if(!file.getName().endsWith(".class")){
                    continue;
                }
                // 全类名 = 包名.类名
                String className = scanPackage + "." + file.getName().replace(".class", "");
                classNames.add(className);
            }
        }

    }


    /**
     * 加载配置文件，读取application.properties配置信息
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {

        InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        doDispatch(req, resp);
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
    }
}
